#include "ReachSharedMemoryRecv.h"
#include "Log.h"

static void *s_shm[SHRM_NUM] = {0};

int r_msg_create_u(int key)
{
    int	msgqid = 0;
    /*IPC_PRIVATE表示自动分配key*/
    msgqid = msgget((key_t)key, IPC_EXCL | 0666);
    if (msgqid >= 0)
        return msgqid;

    msgqid = msgget((key_t)key, IPC_CREAT | 0666);

    LOG_COM_INFO("msgid=%d,key=%d\n", msgqid, key);

    if(msgqid < 0) {
        LOG_COM_ERROR("msgget error, errmsg = %s!\n", strerror(errno));

        return -1;
    }

    return msgqid;
}

int r_msg_recv(int msqid, msgque *msgp, int length, long msgtyp, int msgflg)
{
    int recvlen = 0;

    recvlen = msgrcv(msqid, msgp, length, msgtyp, msgflg);
    return recvlen;

}

int r_msg_send(int msqid, msgque *msgp, int length, int msgflg)
{
    int ret = 0;

    if(-1 == msgp->msgtype) {
        return -1;
    }

    ret = msgsnd(msqid, msgp, length, msgflg);

    return ret;
}

int  get_msg_num(int msgid)
{
    struct  msqid_ds buf;

    if(msgctl(msgid, IPC_STAT, &buf) == -1) {
        return -1;
    }

    return buf.msg_qnum;
}

int r_msg_del(int msgid)
{
    if(msgctl(msgid, IPC_RMID, NULL) == -1) {
        return -1;
    }

    return 0;
}

ReachSharedMemoryRecv::ReachSharedMemoryRecv(QObject *parent)
    : QThread (parent)
    , m_bQuit (false)
    , m_pVideoCallBack (Q_NULLPTR)
{
    qDebug()<<"Enter ReachSharedMemoryRecv";
}

ReachSharedMemoryRecv::~ReachSharedMemoryRecv()
{
    qDebug()<<"Leave ReachSharedMemoryRecv";
}

void ReachSharedMemoryRecv::run()
{
    int msg_rcv_fd	= -1;
    int msg_snd_fd	= -1;
    int msg_id	= 0;
    //int msg_num	= 0;
    int msg_ret = 0;
    msgque msgp;

    int i		= 0;
    int key 	= SHRM_KEY;
    int shmid	= 0;		//共享内存标识符
    void *shm 	= NULL;		//分配的共享内存的原始首地址

    unsigned int nMedia_index	= 0;
    unsigned int nVideo_height	= 0;
    unsigned int nVideo_width	= 0;
    unsigned int nFrameLength	= 0;
    int 		 nShmidx		= 0;
    int 		 nShmkey		= 0;
    unsigned char *nShmptr		= 0;
    MesgQ_Info *pMesgQInfo		= NULL;

    prctl(PR_SET_NAME, "ReceiveData");

    //int nReceivedLength = 0;
    char* szReceiveBuffer = new char[FRAME_MAX_SIZE];
    if (szReceiveBuffer == Q_NULLPTR)
        return;

    PVIDEO_INFO pVideoInfo = new VIDEO_INFO();
    if (pVideoInfo == Q_NULLPTR)
        return;

    while(!m_bQuit) {
        int pCount = 2000;
        QFile flgFileName("/tmp/dirct");

        //1. Check status file
        while (!m_bQuit) {
            if (flgFileName.exists())
                break;
            else {
                this->sleep(1);
                if (pCount-- == 0) {
                    pCount = 2000;
                    LOG_COM_ERROR("------>>>[access file /tmp/dirct]");
                }
            }
        }

        flgFileName.remove();

        //2. Init msgque
        msg_rcv_fd = r_msg_create_u(FRAME_SEND_LINK_KEY + msg_id);
        if(msg_rcv_fd < 0) {
            LOG_COM_ERROR("r_msg_create msg_rcv_fd is failed!");
            continue;
        }

        msg_snd_fd = r_msg_create_u(FRAME_SEND_LINK_KEY + 5 + msg_id);
        if(msg_snd_fd < 0) {
            r_msg_del(msg_rcv_fd);
            LOG_COM_ERROR("r_msg_create msg_snd_fd is failed!");
            continue;
        }

        LOG_COM_INFO("rcvfd = %d, sndfd = %d", msg_rcv_fd, msg_snd_fd);

        //3. Init shared memory
        for(i = 0; i < SHRM_NUM; i++) {
            //创建共享内存
            shmid = shmget((key_t)key, sizeof(MesgQ_Info) + 1920 * 1080 * 3 /2, 0666|IPC_CREAT);
            if(shmid == -1) {
                LOG_COM_ERROR("shmget failed key = 0x%x, i = %d\n", key, i);
                break;
            }

            //将共享内存连接到当前进程的地址空间
            shm = shmat(shmid, 0, 0);
            if(shm == (void*)-1) {
                LOG_COM_ERROR("shmat failed\n");
                break;
            }

            s_shm[i] = shm;

            key++;
        }

        if (key != SHRM_NUM + SHRM_KEY) {
    #if  0
            for (i = 0; i < SHRM_NUM; i++) {
                if (s_shm[i] != NULL) {
                    if(shmdt(s_shm[i]) == -1) {
                        LOG_COM_ERROR("shmdt failed\n");
                    }
                }
            }
            LOG_COM_ERROR("key = %d, ERROR", key);
            r_msg_del(msg_rcv_fd);
            r_msg_del(msg_snd_fd);
    #endif
            continue;
        }

        //4.Data receive loop
        //int stream_fps[8] = {0};
        while (!m_bQuit) {
            try {

                memset(&msgp, 0, sizeof(msgque));
                msg_ret = r_msg_recv(msg_rcv_fd, &msgp, sizeof(msgque) - sizeof(long), 0, 0);
                if(msg_ret < 0) {
                    if (errno == ENOMSG) {
                        static int count = 0;
                        if (count++ > 200) {
                            count = 0;
                            LOG_COM_INFO("ENOMSG.......[%s]", strerror(errno));
                        }
                        //usleep(10*1000);
                        continue;
                    }

                    LOG_COM_ERROR("r_msg_recv data ERROR!!![%s]", strerror(errno));
                    break;
                }

                nShmkey = msgp.msgtype;
                nShmidx = (nShmkey - SHRM_KEY)%SHRM_NUM;
                nShmptr = (unsigned char *)s_shm[nShmidx];

                pMesgQInfo	 = (MesgQ_Info *)nShmptr;
                nMedia_index = pMesgQInfo->chId;
                nVideo_width = pMesgQInfo->width;
                nVideo_height= pMesgQInfo->height;
                nFrameLength = nVideo_width * nVideo_height * 3 / 2;

                pVideoInfo->IFrame          = 0;
                pVideoInfo->VideoFrameRate  = 30;
                pVideoInfo->VideoHeight     = nVideo_height;
                pVideoInfo->VideoWidth      = nVideo_width;
                pVideoInfo->VideoIndex      = nMedia_index;

    #if 0
                stream_fps[nMedia_index]++;

                static int pre_time = get_run_time();

                if ((get_run_time() - pre_time) >= 5000) {
                    for(i = 0; i < 8; i++) {
                        LOG_COM_INFO("------->stream %d, fps = %d", i, stream_fps[i]/5);
                        stream_fps[i] = 0;
                    }

                    pre_time = get_run_time();
                }
    #endif
                if (m_pVideoCallBack)
                    ((video_callback_fun)(m_pVideoCallBack))(nShmptr, nFrameLength, static_cast<void *>(pVideoInfo));

                memset(&msgp, 0, sizeof(msgque));
                msgp.msgtype = (long)nShmkey;
                msgp.msgbuf  = (signed char *)NULL;
                msg_ret = r_msg_send(msg_snd_fd, &msgp, sizeof(msgque) - sizeof(long), IPC_NOWAIT);
                if (msg_ret < 0) {
                    LOG_COM_ERROR("r_msg_send data ERROR!!![%d:%s]", errno, strerror(errno));
                    break;
                }
            } catch(...) {
                LOG_COM_INFO("Try catch error!");
                break;
            }
        }
    }

    //5. Quit
    delete pVideoInfo; pVideoInfo = Q_NULLPTR;
    delete[] szReceiveBuffer; szReceiveBuffer = Q_NULLPTR;

    #if 0
    for (i = 0; i < SHRM_NUM; i++) {
        if (s_shm[i] != NULL) {
            if(shmdt(s_shm[i]) == -1) {
                LOG_COM_ERROR("shmdt failed\n");
            }
        }
    }

    r_msg_del(msg_rcv_fd);
    r_msg_del(msg_snd_fd);
#endif
}

void ReachSharedMemoryRecv::startSharedMemoryRecv(void *funCB)
{    
    if (!this->isRunning())
    {
        qDebug()<<"---------> start sharred memary recv";
        m_pVideoCallBack = funCB;
        start();
    }
}

void ReachSharedMemoryRecv::quitSharedMemoryRecv()
{
    m_bQuit = true;
    this->wait();
}
